package com.wechat.page.contact.select;

import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

import com.swak.entity.Toggle;
import com.swak.entity.YesNo;
import com.swak.fx.support.AbstractPage;
import com.swak.fx.support.FXMLView;
import com.swak.fx.support.ui.UI;
import com.swak.fx.support.ui.UIScrollPane;
import com.swak.fx.support.ui.UISearchBar;
import com.swak.utils.Maps;
import com.swak.utils.StringUtils;
import com.wechat.dto.ContactDTO;
import com.wechat.enums.ContactType;
import com.wechat.page.contact.card.ContactCardComp;
import com.wechat.page.contact.misc.Title;

import javafx.beans.property.ListProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;

/**
 * 联系人选择
 * 
 * @author lifeng
 */
@FXMLView(title = "联系人选择-菜单", value = "ContactSelectSide.fxml", css = "ContactSelectSide.css", stageStyle = "TRANSPARENT")
public class ContactSelectSide extends AbstractPage {

	public static final String ToggleEvent = "Toggle.Event";

	// ========================
	// 搜索
	// ========================
	private UISearchBar searchInput;

	// ========================
	// 列表数据
	// ========================
	private VBox contacts;
	private UIScrollPane contactsScroll;

	public ContactSelectSide() {
		this.searchInput = new UISearchBar();
		this.contactsScroll = new UIScrollPane();
		this.contacts = new VBox();
	}

	@Override
	public void initializeParts() {
		super.initializeParts();
		this.initializeSearchParts();
		this.initializeContactsParts();
	}

	private void initializeSearchParts() {
		this.searchInput.clear();
		this.searchInput.getStyleClass().add("search");
		this.addChild(this.searchInput);
		GridPane.setConstraints(this.contactsScroll, 0, 0);
		GridPane.setHgrow(this.searchInput, Priority.ALWAYS);
		GridPane.setMargin(this.searchInput, new Insets(10, 10, 10, 10));
	}

	private void initializeContactsParts() {
		this.contacts.setAlignment(Pos.CENTER);
		this.contacts.getStyleClass().add("list-page__inner");
		this.contactsScroll.setContent(this.contacts);
		this.contactsScroll.setFitToWidth(true);
		this.contactsScroll.setHbarPolicy(ScrollBarPolicy.NEVER);
		this.contactsScroll.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
		this.addChild(this.contactsScroll);
		GridPane.setConstraints(this.contactsScroll, 0, 1);
		GridPane.setHgrow(this.contactsScroll, Priority.ALWAYS);
		GridPane.setVgrow(this.contactsScroll, Priority.ALWAYS);
	}

	@Override
	public void setupValueChangedListeners() {
		super.setupValueChangedListeners();
		this.contactsProperty().addListener(this::onContactsChange);
		this.activeProperty().addListener(this::onContactActive);
	}

	private void onContactActive(ObservableValue<? extends ContactDTO> ob, ContactDTO unActive, ContactDTO active) {
		if (unActive != null) {
			Node remove = this.findContactNode(unActive);
			if (remove != null) {
				remove.getStyleClass().remove("active");
			}
		}
		if (active != null) {
			Node remove = this.findContactNode(active);
			if (remove != null) {
				remove.getStyleClass().add("active");
			}
		}
	}

	@SuppressWarnings("unchecked")
	private void onContactsChange(Change<? extends Toggle<ContactDTO>> c) {
		while (c.next()) {
			if (c.wasReplaced()) {
				List<Toggle<ContactDTO>> adds = (List<Toggle<ContactDTO>>) c.getAddedSubList();
				for (Toggle<ContactDTO> contact : adds) {
					this.onUpdateContact(contact.getValue(), contact.isActive());
				}
			} else if (c.wasAdded()) {
				List<Toggle<ContactDTO>> adds = (List<Toggle<ContactDTO>>) c.getAddedSubList();
				for (Toggle<ContactDTO> contact : adds) {
					this.onAddContact(contact.getValue());
				}
			} else if (c.wasRemoved()) {
				List<Toggle<ContactDTO>> adds = (List<Toggle<ContactDTO>>) c.getRemoved();
				for (Toggle<ContactDTO> contact : adds) {
					this.onRemoveContact(contact.getValue());
				}
			}
		}
	}

	private void onUpdateContact(ContactDTO contact, boolean active) {
		if (!this.hasContact(contact)) {
			return;
		}
		StackPane node = this.findContactNode(contact);
		ContactCardComp messageThumb = (ContactCardComp) node.getProperties().get(UI.Conts.Comp_Key);
		messageThumb.activeProperty().set(active);
	}

	private void onAddContact(ContactDTO contact) {

		// 只能添加一次
		if (this.hasContact(contact)) {
			return;
		}

		// 实例化界面
		StackPane inner = new StackPane();
		inner.getStyleClass().add("list-page__ph");
		ContactCardComp cardThumb = ContactCardComp.New(ContactCardComp.class, this);
		cardThumb.enableSelectProperty().set(true);
		cardThumb.$on(ContactCardComp.ToggleEvent, onContactSelectConsumer);
		inner.getChildren().add(cardThumb.getRoot());
		inner.getProperties().put(UI.Conts.ID_Key, contact.getId());
		inner.getProperties().put(UI.Conts.Data_Key, contact);
		inner.getProperties().put(UI.Conts.Comp_Key, cardThumb);
		inner.getProperties().put(UI.Conts.Type_Key, this.getType(contact));

		// 保存列表实例
		this.saveContactNode(contact, inner);

		// 添加到列表
		this.Add2Box(inner);

		// 设置 Chat
		this.$runOnUI(() -> {
			cardThumb.contactProperty().set(contact);
		});
	}

	private String getType(ContactDTO contact) {
		if (contact.getType() != null && contact.getType() == ContactType.Assistant.getType()) {
			return "助手";
		} else if (contact.getType() != null && contact.getType() == ContactType.Chat_Room.getType()) {
			return "群聊";
		} else if (contact.getIsStar() != null && contact.getIsStar() == YesNo.Yes.getType()) {
			return "星标朋友";
		}
		return contact.getPyInitial();
	}

	private void Add2Box(Node node) {
		Object nodeType = node.getProperties().getOrDefault(UI.Conts.Type_Key, StringUtils.EMPTY);
		List<Node> nodes = this.contacts.getChildren();
		int index = -1;
		for (int i = 0; i < nodes.size(); i++) {
			Node child = nodes.get(i);
			Object childType = child.hasProperties() ? child.getProperties().get(UI.Conts.Type_Key) : StringUtils.EMPTY;
			if (nodeType != null && childType != null && childType.equals(nodeType)) {
				index = i;
				continue;
			}

			// find the last index
			if (index != -1) {
				break;
			}
		}

		// 没有找到相关的分类
		if (index == -1) {
			Title typeTitle = new Title(nodeType.toString());
			typeTitle.getProperties().put(UI.Conts.ID_Key, nodeType);
			typeTitle.getProperties().put(UI.Conts.Type_Key, nodeType);
			this.contacts.getChildren().add(typeTitle);
			this.contacts.getChildren().add(node);
		}

		// 找到位置
		else {
			this.contacts.getChildren().add(index, node);
		}
	}

	private void onRemoveContact(ContactDTO contact) {
		StackPane node = this.findContactNode(contact);
		if (node != null) {
			ContactCardComp messageThumb = (ContactCardComp) node.getProperties().get(UI.Conts.Comp_Key);
			messageThumb.destory();
			this.contacts.getChildren().remove(node);
		}
	}

	@SuppressWarnings("unchecked")
	private void onContactSelect(Object o) {
		Toggle<Object> toggle = (Toggle<Object>) o;
		this.$emit(ToggleEvent, toggle);
		this.$runUI(() -> this.activeProperty().set((ContactDTO) toggle.getValue()));
	}

	private Consumer<Object> onContactSelectConsumer = this::onContactSelect;

	// ================================================================================
	// Chat <==> Pane
	// ================================================================================
	private Map<String, StackPane> contactPanes = Maps.newHashMap();

	private synchronized boolean hasContact(ContactDTO chat) {
		return contactPanes.containsKey(chat.getId());
	}

	private synchronized void saveContactNode(ContactDTO chat, StackPane inner) {
		this.contactPanes.put(chat.getId(), inner);
	}

	private synchronized StackPane findContactNode(ContactDTO chat) {
		return this.contactPanes.get(chat.getId());
	}

	// ================================================================================
	// Properties
	// ================================================================================
	private final ListProperty<Toggle<ContactDTO>> contactsProperty = new SimpleListProperty<>(
			FXCollections.observableArrayList());

	public ListProperty<Toggle<ContactDTO>> contactsProperty() {
		return contactsProperty;
	}

	private final ObjectProperty<ContactDTO> activeProperty = new SimpleObjectProperty<>();

	public ObjectProperty<ContactDTO> activeProperty() {
		return activeProperty;
	}
}
